﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Mls
{
    class RawFileParser : AbstractParser
    {
        public struct Glyph
        {
            public int FilePosition;
            public int ShowingPosition;
            public Character OriginalCharacter;
            public string Text;
        }

        public struct Error
        {
            public int FilePosition;
            public int ShowingPosition;
            public int ShowingLength;
            public string ErrorType;
            public string ErrorMessage;

            public override string ToString()
            {
                return string.Format("[{0}- {1}]: {2}", FilePosition, ErrorType, ErrorMessage);
            }
        }

        private List<Glyph> _glyphs = new List<Glyph>();
        public List<Glyph> Glyphs { get { return _glyphs; } }

        private List<Error> _errors = new List<Error>();
        public List<Error> Errors { get { return _errors; } }

        public Glyph FindGlyph(int rtfPosition)
        {
            for (int i = 0; i < _glyphs.Count; i++)
            {
                if (rtfPosition > _glyphs[i].ShowingPosition)
                    continue;

                if (rtfPosition == _glyphs[i].ShowingPosition)
                    return _glyphs[i];

                if (rtfPosition < _glyphs[i].ShowingPosition && i > 0 && rtfPosition > _glyphs[i - 1].ShowingPosition)
                    return _glyphs[i - 1];

                throw new InvalidProgramException();
            }

            throw new InvalidProgramException();
        }

        private int _currentPosition = 0;

        Smartee.RichText.VisitorAndBuilder.Builder builder = new Smartee.RichText.VisitorAndBuilder.Builder();

        private bool _isCoptic = false;

        private int _defaultFontId;
        private int _copticFontId;

        private const int _defaultColorId = 0;
        private int _redColorId;
        private int _cyanColorId;
        private int _blueColorId;
        private int _greenColorId;
        private int _purpuleColorId;
        private int _yellowColorId;

        public RawFileParser()
        {
        }

        public void ReadFile(Document document, out string rtf)
        {
            try
            {
                _glyphs.Clear();
                _errors.Clear();
                _currentPosition = 0;

                builder.StartDocument();

                builder.StartFontTable();
                _defaultFontId = builder.AddFont("swiss", "Arial");
                _copticFontId = builder.AddFont("swiss", "ArialCoptic");
                builder.EndFontTable();

                builder.StartColorTable();
                _redColorId = builder.AddColor(byte.MaxValue, 0, 0);
                _cyanColorId = builder.AddColor(0, byte.MaxValue, byte.MaxValue);
                _blueColorId = builder.AddColor(0, 0, byte.MaxValue);
                _greenColorId = builder.AddColor(0, byte.MaxValue, 0);
                _purpuleColorId = builder.AddColor(byte.MaxValue, 0, byte.MaxValue);
                _yellowColorId = builder.AddColor(byte.MaxValue, byte.MaxValue, 0);
                builder.EndColorTable();

                _isCoptic = false;

                builder.StartParagraph(Smartee.RichText.Justification.Left, Smartee.RichText.TextDirection.LeftToRight);
                base.Parse(document);
                builder.EndParagraph();

                StringWriter text = new StringWriter();
                Smartee.RichText.VisitorAndBuilder.RtfWriter writer = new Smartee.RichText.VisitorAndBuilder.RtfWriter(text);
                builder.EndDocument().Accept(writer);

                rtf = text.ToString();
            }
            catch
            {
                rtf = null;
                _errors.Clear();
                _errors.Add(new Error() { ErrorMessage = "Cannot parse file", ErrorType = "Fatal" });
            }
        }

        private void SetCoptic(bool isCoptic)
        {
            if (_isCoptic != isCoptic)
            {
                _isCoptic = isCoptic;
                builder.FontId = isCoptic ? _copticFontId : _defaultFontId;
            }
        }

        protected override void UnexpectedCharacters(Character[] text, string errorMessage)
        {
            int startingPosition = _currentPosition;

            builder.ForegoundColorId = _redColorId;
            AddCharacters(text);
            builder.ForegoundColorId = _defaultColorId;

            _errors.Add(new Error() { FilePosition = _glyphs.Count, ShowingPosition = startingPosition, ShowingLength = _currentPosition - startingPosition,
                ErrorType = "Unexpected Characters '" + GetText(text) + "'", ErrorMessage = errorMessage });
        }

        protected override void StartNewCell(Character[] text)
        {
            AddCharacters(text);
        }

        protected override void IntercommandsWhitespaces(Character[] whitespaces)
        {
            AddCharacters(whitespaces);
        }

        protected override void EndRow(Character[] text)
        {
            AddCharacters(text);
        }

        protected override void ParagraphSeparator(Character text)
        {
            builder.EndParagraph();
            builder.StartParagraph(Smartee.RichText.Justification.Left, Smartee.RichText.TextDirection.LeftToRight);

            _glyphs.Add(new Glyph() { ShowingPosition = _currentPosition, FilePosition = _glyphs.Count, OriginalCharacter = text, Text = "\n" } );
            _currentPosition += _glyphs.Last().Text.Length;
        }

        protected override void ParagraphCharacter(Character character)
        {
            AddCharacters(new Character[] { character });
        }

        protected override void PageNumber(Character character)
        {
            AddCharacters(new Character[] { character });
        }

        protected override void StartEmbed(Character character)
        {
            AddCharacters(new Character[] { character });
        }

        protected override void EndEmbed(Character character)
        {
            AddCharacters(new Character[] { character });
        }

        protected override void JustifyCommand(Character[] commandText, Smartee.RichText.Justification justify)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void BoldCommand(Character[] commandText, bool isBold)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void FontCommand(Character[] commandText, Language.Type language, int fontId)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void HeaderCommand(Character[] commandText)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void FooterCommand(Character[] commandText)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void TableCommand(Character[] commandText, ColumnDefinition[] definition)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void PageAdvanceCommand(Character[] commandText)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void PageNumberCommand(Character[] commandText, int newPageNumber)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void PageNumberCodeCommand(Character[] commandText)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void FootnoteCommand(Character[] commandText)
        {
            builder.ForegoundColorId = _blueColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void InvalidCommandArguments(Character[] commandText)
        {
            int startingPosition = _currentPosition;

            builder.ForegoundColorId = _redColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;

            _errors.Add(new Error() { FilePosition = _glyphs.Count, ShowingPosition = startingPosition, ShowingLength = _currentPosition - startingPosition,
                ErrorType = "Invalid Command Arguments", ErrorMessage = GetText(commandText) });
        }

        protected override void NotHandledCommand(Character[] commandText)
        {
            builder.ForegoundColorId = _cyanColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;
        }

        protected override void UnknownCommand(Character[] commandText)
        {
            int startingPosition = _currentPosition;

            builder.ForegoundColorId = _redColorId;
            AddCharacters(commandText);
            builder.ForegoundColorId = _defaultColorId;

            _errors.Add(new Error() { FilePosition = _glyphs.Count, ShowingPosition = startingPosition, ShowingLength = _currentPosition - startingPosition,
                ErrorType = "Unknown Command", ErrorMessage = GetText(commandText) });
        }

        private string GetText(Character[] commandText)
        {
            string text = string.Empty;
            foreach (Character character in commandText)
                text += CharactersMapper.Map(character);
            return text;
        }

        private void AddCharacters(Character[] text)
        {
            foreach (Character character in text)
            {
                if (character.Type == Token.NewLine)
                {
                    ParagraphSeparator(character);
                }
                else
                {
                    _glyphs.Add(new Glyph() { ShowingPosition = _currentPosition, FilePosition = _glyphs.Count, OriginalCharacter = character, Text = CharactersMapper.Map(character) });
                    _currentPosition += _glyphs.Last().Text.Length;

                    if (character.Type == Token.Delimiter ||
                        character.Type == Token.Embed)
                    {
                        builder.ForegoundColorId = _purpuleColorId;
                    }
                    SetCoptic(character.Language.IsCoptic);
                    builder.InsertText(_glyphs.Last().Text);
                    if (character.Type == Token.Delimiter ||
                        character.Type == Token.Embed)
                    {
                        builder.ForegoundColorId = _defaultColorId;
                    }
                }
            }
        }

        protected override void StartCommandBlock()
        {
        }
    }
}
